/**
 * Connects to Facebook using FacebookXmlRestClient and provides methods to download user data.
 * Uses FBLoader to load images in separate thread.
 * 
 * @author Antti Vuorela
 */

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import processing.core.*;

import com.google.code.facebookapi.*;

public class FBConnect {
	
	private static String API_KEY = "5fdef01c17aadc368804d0f4b11f968f";
	private static String SECRET = "a0cbd82ea18060266f7e7b958c94a452";
	
	public FBLoader loader;
	public Thread loadThread;
	public FacebookXmlRestClient client;
	
	private PApplet parent;
	private String token;
	private String session;
	private Long userId;
	
	private ColorCalculator c_calc;
	
	public ProfileImage user = null;
	public ImageCatalog catalog = null;
	
	/**
	 * Constructor generates new FacebookXmlRestClient
	 * 
	 * @param parent
	 */
	public FBConnect(PApplet parent) {
		this.parent = parent;
		this.c_calc = new ColorCalculator(this.parent);
		this.client = new FacebookXmlRestClient(API_KEY, SECRET);
	}
	
	/**
	 * Opens the Facebook login window in users default browser to authorize app
	 */
	public boolean login(){
		
		try {
			this.token = this.client.auth_createToken();
		} catch (Exception e) {
			return false;
		}
		
		System.out.println("Auth token: "+ this.token);
		String url = "http://www.facebook.com/login.php?api_key=" + API_KEY 
			+ "&v=1.0+&auth_token="+token;
		
		this.parent.link(url, "_new"); // PApplet's link method
		return true;
	}
	
	/**
	 * Get's the session key after user have logged in
	 */
	public boolean startSession(){
		
		try {
			this.session = client.auth_getSession(token); //,true
			this.userId = client.users_getLoggedInUser();
		} catch (Exception e) {
			return false;
		} 
		
		//String secret = client.getCacheSessionSecret();
		//System.out.println("Cache secret is " + secret);
		System.out.println("Starting session succeed. Session key is " + this.session);	
		System.out.println("Logged in User id is " + this.userId);
		return true;
	}
	
	/**
	 * Gets users name and urls for pictures
	 * @param users
	 * @return ProfileImage with that data
	 */
	public ArrayList<ProfileImage> getUserInfo(Collection<Long> users){
		
		Collection<ProfileField> fields = new ArrayList<ProfileField>();
		fields.add(ProfileField.NAME);
		fields.add(ProfileField.PIC_SQUARE);
		fields.add(ProfileField.PIC_BIG);
		
		Document data;
		
		try {
			 data = client.users_getInfo(users,fields);
		} catch (FacebookException e) {
			System.err.println("Could not download user info");
			return null;
		}
		
		NodeList userNodes = data.getElementsByTagName("user");
		ArrayList<ProfileImage> profileImages = new ArrayList<ProfileImage>();
		
		
		for (int i = 0; (i < userNodes.getLength()); i++){
			
			Element userData = (Element)userNodes.item(i);
			
			Long uid = Long.parseLong(userData.getElementsByTagName("uid").item(0).getTextContent());
			String name = userData.getElementsByTagName("name").item(0).getTextContent();
			String pic_square_url = userData.getElementsByTagName("pic_square").item(0).getTextContent();
			String pic_big_url = userData.getElementsByTagName("pic_big").item(0).getTextContent();
			
			if (pic_square_url != null){
				ProfileImage user = new ProfileImage(uid,name,pic_square_url,pic_big_url,c_calc);
				profileImages.add(user);
			}
		}
		
		return profileImages;
		
	}
	
	/**
	 * Returns users friends
	 * 
	 * @return Collection of friends UID:s
	 */
	public Collection<Long> getFriends(){
		
		Collection<Long> friends = new LinkedList<Long>();
		
		NodeList friendNodes;
		
		try {
			friendNodes = client.friends_get().getElementsByTagName("uid");
		} catch (FacebookException e) {
			System.err.println("Could't load friends");
			return null;
		}
		
		for (int i = 0; i < friendNodes.getLength(); i++){
			Element friend = (Element)friendNodes.item(i);
			friends.add(Long.valueOf(friend.getTextContent()));
		}
		
		return friends;
	}
	
	/**
	 * Get the loading status as percents
	 * @return
	 */
	public int loadingPercent(){
		int[] status = this.loader.loadingStatus();
		int percent = 100-(status[0]*100)/status[1];
		return percent;
	}
	
	/**
	 * Starts to load logged in user's image
	 */
	public void loadUserImage(){
		
		Collection<Long> users = new LinkedList<Long>();
        users.add(userId);
        
		this.user = getUserInfo(users).get(0);
		this.loadBigImage(this.user);
	}
	
	/**
	 * 
	 * @return
	 */
	public ProfileImage getUserImage(){
		if (this.loader.isLoading()){
			return null;
		} else {
			return this.user;
		}	
	}
	
	/**
	 * Load big image
	 */
	public void loadBigImage(ProfileImage user){
		this.loader = new FBLoader(this,this.parent);
		this.loadThread = new Thread(loader);
		this.loader.loadPic(user,ProfileField.PIC_BIG);
		this.loadThread.start();
	}
	
	/**
	 * Starts to load Image Catalog of friends
	 */
	public void loadImageCatalog(){
		
		loader = new FBLoader(this,this.parent);
		loadThread = new Thread(loader);
		
		Collection<Long> friends = this.getFriends();
		ArrayList<ProfileImage> info = getUserInfo(friends);
		this.catalog = new ImageCatalog();
		
		// add current user to catalog, too
		info.add(user);
		
		for (int i=0; i<info.size(); i++){
			ProfileImage friend = info.get(i);
			if (friend.pic_big_url != ""){
				this.loader.loadPic(friend, ProfileField.PIC_SQUARE);
				this.catalog.addImage(friend);
			}
		}
		
		this.loadThread.start();
	}

	/**
	 * Returns the <code>ImageCatalog</code> downloaded for the specified user. If loading 
	 * isn't ready, returns <code>null</code>.
	 * 
	 * @return	<code>ImageCatalog</code> of user's friend, or <code>null</code> if
	 * 			downloading isn't ready
	 */
	public ImageCatalog getImageCatalog(){
		
		if (this.loader.isLoading()){
			return null;
		} else {
			return this.catalog;
		}
	}
	
}
